package com.coalmine.api.servelt;

import cn.hutool.core.util.StrUtil;
import com.alibaba.druid.pool.DruidPooledConnection;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.aventrix.jnanoid.jnanoid.NanoIdUtils;
import com.coalmine.api.common.PageBean;
import com.coalmine.api.common.ResponseDto;
import com.coalmine.api.domain.*;
import com.coalmine.api.enums.EApiPermission;
import com.coalmine.api.enums.EApiStatus;
import com.coalmine.api.enums.EApproveStatus;
import com.coalmine.api.mapper.ApiApproveMapper;
import com.coalmine.api.plugin.CachePlugin;
import com.coalmine.api.plugin.PluginManager;
import com.coalmine.api.plugin.TransResultSetPlugin;
import com.coalmine.api.service.*;
import com.coalmine.api.service.impl.ApiService;
import com.coalmine.api.service.impl.MailService;
import com.coalmine.api.util.*;
import com.coalmine.common.constant.CacheConstants;
import com.coalmine.common.constant.CacheTimeConstants;
import com.coalmine.common.constant.Constants;
import com.coalmine.common.core.redis.RedisCache;
import com.coalmine.common.utils.DateUtils;
import com.coalmine.common.utils.sql.SqlUtil;
import com.github.freakchick.orange.SqlMeta;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
public class APIServlet extends HttpServlet {
    static ConcurrentHashMap<String, List<ApiLog>> map = new ConcurrentHashMap<>();
    @Autowired
    private RedisCache redisCache;

    @Autowired
    IApiConfigService apiConfigService;
    @Autowired
    IApiDatasourceService dataSourceService;
    @Autowired
    ApiService apiService;

    @Autowired
    private ApiApproveMapper approveMapper;

    @Autowired
    IApiTokenService tokenService;
    @Autowired
    IApiIpRulesService ipService;
    @Autowired
    MailService mailService;
    @Autowired
    IApiLogService apiLogService;


    @Value("${datamanage.api.context}")
    String apiContext;
    @Value("${datamanage.api.result}")
    String apiResult;
    @Value("${datamanage.api.set}")
    String apiResultSet;
    @Value("${datamanage.api.maxVisitNum}")
    Integer maxVisitNum;
    @Value("${datamanage.api.batchSubNum}")
    Integer batchSubNum;
    @Autowired
    private KafkaTemplate kafkaTemplate;

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) {
        String servletPath = request.getRequestURI();
        servletPath = servletPath.substring(apiContext.length() + 2);
        // 查询请求地址是否存在 去除/query字符串
        if (servletPath.contains(apiResult)) {//query路径
            servletPath = servletPath.substring(apiResult.length() + 1);
        }
        if (servletPath.contains(apiResultSet)) {//dtd路径
            servletPath = servletPath.substring(apiResultSet.length() + 1);
        }
        PrintWriter out = null;
        try {
            out = response.getWriter();
            String responseDto = process(servletPath, request, response);
            out.append(responseDto);
        } catch (Exception e) {
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            out.append(ResponseDto.fail(e.getMessage()).toJsonString());
            log.error(e.toString(), e);
        } finally {
            if (out != null)
                out.close();
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
        doGet(req, resp);
    }

    public String process(String path, HttpServletRequest request, HttpServletResponse response) throws InterruptedException {

        String token = StrUtil.isBlank(request.getParameter("token")) ? request.getHeader("token") : request.getParameter("token");
        //校验接口是否存在
        ApiConfig config = apiConfigService.getConfig(path);
        // 接口状态校验
        if (EApiStatus.OFFLINE.getCode() == config.getStatus()) {
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return ResponseDto.failWithCodeMsg(HttpServletResponse.SC_NOT_FOUND, "资源已下线，请联系管理员").toJsonString();
        }
        //接口限流
        String url = String.valueOf(request.getRequestURL());
        String key = url;
        if (config.getPrevilege() == EApiPermission.PRIVATE.getCode()
                && StringUtils.isNotBlank(token)) {
            key = url + token;
        }
        //每5秒访问次数maxVisitNum
        boolean flag = redisCache.sliderRateLimit(Constants.RATE_LIMIT_KEY + key, 1, maxVisitNum);
        if (!flag) {
            return ResponseDto.failWithCodeMsg(HttpServletResponse.SC_FORBIDDEN, "请不要频繁访问接口").toJsonString();
        }

        // 校验接口是否已申请并审批通过
        // TODO P2: 兼容历史，暂时不对api层的审批校验
        if (!checkApiApproved(config, token)) {
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
            return ResponseDto.failWithCodeMsg(HttpServletResponse.SC_FORBIDDEN, "资源未申请或未审批通过，请联系管理员处理").toJsonString();
        }
        //校验数据源是否存在
        ApiDatasource datasource = dataSourceService.detail(config.getDatasourceId());
        if (datasource == null) {
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return ResponseDto.failWithCodeMsg(HttpServletResponse.SC_NOT_FOUND, "数据源不存在，请联系管理员").toJsonString();
        }

        //redis 数据源
        if (com.coalmine.common.utils.StringUtils.equals("redis", datasource.getType())) {
            /*List<Object> redisObject = JdbcUtil.jdbcGetRedisObject(datasource, config);
            Object obj = redisObject;
            //如果只有一条数据，返回对象形式，就不是数组格式
            if (!redisObject.isEmpty() && redisObject.size() == 1) {
                obj = redisObject.get(0);
            }*/
            String redisResult = RedisPoolManager.setOrGetRedisByKey(datasource, config);
            return redisResult;

        }

        //校验参数
        String realServletPath = request.getRequestURI();
        Map<String, Object> sqlParam = getParams(request, config);
        //if (!realServletPath.contains(apiResultSet)) {
        //sqlParam = getParams(request, config);
        //}
        //SQL参数非法字符校验
        boolean LimitFlag = SqlUtil.sqlParamValidate(sqlParam);
        if (LimitFlag) {
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            return ResponseDto.failWithCodeMsg(HttpServletResponse.SC_BAD_REQUEST, "参数可能存在SQL注入风险").toJsonString();
        }
        config.setRealServletPath(realServletPath);
        try {
            String originIp = IPUtil.getOriginIp(request);
            //从缓存获取数据
            if (config != null && StringUtils.isNotBlank(config.getCachePlugin())) {
                CachePlugin cachePlugin = PluginManager.getCachePlugin(config.getCachePlugin());
                Object object = cachePlugin.get(config, sqlParam);
                if (object != null) {
                    if (!realServletPath.contains(apiResultSet)) {//不是dtd
                        //记录api日志
                        /*AsyncUtils.asyncExecute(() -> {
                            saveApiLog(config, token, originIp, true, "成功");
                        });*/
                        //内部接口不写日志
                        if (EApiPermission.INNER.getCode() != config.getPrevilege()) {
                            saveApiLog(config, token, originIp, true, "成功");
                        }
                    }
                    //如果缓存有数据直接返回
                    if (realServletPath.contains(apiResult) || realServletPath.contains(apiResultSet)) {
                        return ResponseDto.apiSuccess(object).toQueryString();
                    } else {//
                        return JSON.toJSONString(object, SerializerFeature.WriteMapNullValue);
                        //ResponseDto.apiSuccess(object).toJsonString();
                    }
                }
            }

            if (!realServletPath.contains(apiResultSet)) {
                //执行sql,设置缓存
                Object resObj = setResultPlugin(config, sqlParam, datasource);
                if (hasPaging(sqlParam)) {//如果有分页
                    Integer total = getTotalCount(config, sqlParam, datasource);
                    PageBean pageBean = new PageBean();
                    Integer pageNum = ObjectUtils.allNull((sqlParam.get("pageNum"))) == true ? 1 : Integer.parseInt(String.valueOf(sqlParam.get("pageNum")));
                    Integer pageSize = ObjectUtils.allNull((sqlParam.get("pageSize"))) == true ? 10 : Integer.parseInt(String.valueOf(sqlParam.get("pageSize")));
                    pageBean.setCurrentPage(pageNum);
                    pageBean.setPageSize(pageSize);
                    pageBean.setTotalCount(total);
                    pageBean.setRows(resObj);
                    ResponseDto dto = ResponseDto.apiSuccess(pageBean);
                    String res = JSON.toJSONString(dto, SerializerFeature.WriteMapNullValue);
                    return res;
                }
                ResponseDto dto = ResponseDto.apiSuccess(resObj);
                ApiConfig finalConfig = config;
                //记录api日志
                if (EApiPermission.INNER.getCode() != config.getPrevilege()) {
                    saveApiLog(finalConfig, token, originIp, true, "成功");
                }
                if (realServletPath.contains("push")) {
                    String topic = realServletPath.substring(realServletPath.indexOf("push") + 5);
                    kafkaTemplate.send(topic.replace("/", "_"), JSONObject.toJSONString(sqlParam));
                }
                if (realServletPath.contains(apiResult)) {
                    //返回data
                    return dto.toQueryString();
                } else {
                    //返回code,msg，data
                    return JSON.toJSONString(dto, SerializerFeature.WriteMapNullValue);
                }

            }else {
                //返回字段注释dtd结果集
                ResponseDto dtdResponse = getResponseDtD(config,sqlParam,datasource);
                return dtdResponse.toQueryString();
            }

        } catch (Exception e) {
            log.error("sql=====>参数" + config.getSqlList());
            log.error("参数=====>" + sqlParam);
            //log.error("api执行异常 ==>" + e);
            //如果API配置了邮件告警
            /*if (config != null && StringUtils.isNotBlank(config.getMail())) {
                String title = MessageFormat.format("API ERROR: {0}", config.getName());
                String content = MessageFormat.format("TIME:  {0}\n NAME:  {1}\n URL:  {2}\n REMOTE ADDRESS:  {3}\n\n{4}",
                        DateUtil.now(),
                        config.getName(), request.getRequestURI(), request.getRemoteAddr(), e.toString());
                mailService.sendSimpleMail(config.getMail().split(";"), title, content);
            }*/
            String errorMsg = "API调用失败";
            try {
                String originIp = IPUtil.getOriginIp(request);
                if (e != null && e.getMessage() != null) {
                    errorMsg = JSONObject.toJSONString(e.getMessage());
                }
                //内部接口不记录日志
                if (EApiPermission.INNER.getCode() != config.getPrevilege()) {
                    saveApiLog(config, token, originIp, false, errorMsg);
                }
            } catch (IOException ioException) {
                ioException.printStackTrace();
            }
            ResponseDto dtdResponse = ResponseDto.fail("API调用失败，请检查参数");
            return dtdResponse.toJsonString();
            // throw new RuntimeException("api调用异常");
        }
    }

    private ResponseDto getResponseDtD(ApiConfig config, Map<String, Object> sqlParam,
                                       ApiDatasource datasource) throws SQLException {
        //返回dtd结果集
        List<ApiSql> apiSqlList = config.getSqlList();
        List<List<Map<String, String>>> apiResultList = new ArrayList<>();
        List<Map<String, String>> apiResult = new ArrayList<>();
        for (ApiSql apiSql : apiSqlList) {
            apiResult = JdbcUtil.getSqlResult(datasource, apiSql.getSqlText(), sqlParam);
            apiResultList.add(apiResult);
        }
        ResponseDto dtdResponse = ResponseDto.apiSuccess(JSONObject.toJSONString(apiResult));
        if (!apiSqlList.isEmpty() && apiSqlList.size() > 1) {
            //返回集合
            dtdResponse = ResponseDto.apiSuccess(JSONObject.toJSONString(apiResultList));
        }
        //设置缓存
        setRedisCache(config, sqlParam, dtdResponse.toQueryString());
        return dtdResponse;
    }

    private void saveApiLog(ApiConfig config, String tokenStr, String originIp, Boolean result, String msg) {
        ApiLog apiLog = new ApiLog();
        apiLog.setId(NanoIdUtils.randomNanoId());
        apiLog.setApiId(config.getId());
        //String tokenStr = request.getParameter("token");
        if (StringUtils.isNotBlank(tokenStr)) {
            ApiToken apiToken = tokenService.getToken(tokenStr);
            if (apiToken != null) {
                apiLog.setTokenId(apiToken.getId());
            }
        }
        apiLog.setIp(originIp);
        apiLog.setTag(result);
        apiLog.setMsg(msg);
        apiLog.setCreateTime(new Date());
        apiStatByDay(apiLog);
        //Constants.apiLogList.add(apiLog);
        List<ApiLog> apiLogList = map.get(Constants.API_LOG_KEY);
        if (apiLogList == null) {
            apiLogList = new ArrayList<>();
        }
        apiLogList.add(apiLog);
        //API每100条批量提交日志
        map.put(Constants.API_LOG_KEY, apiLogList);
        if (apiLogList != null && apiLogList.size() > 0
                && apiLogList.size() % batchSubNum == 0) {
            try {
                map.clear();

                apiLogService.insertApiLogList(apiLogList);
            } catch (Exception e) {
                //redisCache.deleteObject(Constants.API_LOG_KEY);
                e.printStackTrace();
            }
        }
    }


  private Object setResultPlugin(ApiConfig config, Map<String,
          Object> sqlParam, ApiDatasource datasource){
      List<ApiSql> sqlList = config.getSqlList();
      DruidPooledConnection connection = null;
      try {
          connection = PoolManager.getPooledConnection(datasource);
      } catch (SQLException throwables) {
          throwables.printStackTrace();
      }
      //是否开启事务
      boolean flag = config.getOpenTrans() == 1 ? true : false;
      //执行sql
      List<Object> dataList = executeSql(connection, sqlList, sqlParam, flag);
      //log.info("dataList {}", dataList);
      //执行数据转换
      for (int i = 0; i < sqlList.size(); i++) {
          ApiSql apiSql = sqlList.get(i);
          Object data = dataList.get(i);
          //如果此单条sql是查询类sql，并且配置了数据转换插件
          if (data instanceof Iterable && StringUtils.isNotBlank(apiSql.getTransformPlugin())) {
              //log.info("transform plugin execute");
              List<JSONObject> sourceData = (List<JSONObject>) (data); //查询类sql的返回结果才可以这样强制转换，只有查询类sql才可以配置转换插件
              //TransformPlugin transformPlugin = PluginManager.getTransformPlugin(apiSql.getTransformPlugin());
              //Object resData = transformPlugin.transform(sourceData, apiSql.getTransformPluginParams());
              TransResultSetPlugin transResultPlugin = PluginManager.getTransResultSetPlugin(apiSql.getTransformPlugin());
              Object resData = transResultPlugin.transResultSet(sourceData, apiSql.getTransformPluginParams());
              dataList.set(i, resData);//重新设置值
          }
      }
      Object res = dataList;
      //如果只有单条sql,返回结果不是数组格式
      if (!dataList.isEmpty() && dataList.size() == 1) {
          res = dataList.get(0);
          //List<JSONObject> resList = (List<JSONObject>) dataList.get(0);
          //res = resList.get(0);
      }
      //设置缓存
      setRedisCache(config, sqlParam, res);
      return res;
  }
    //设置缓存
    private void setRedisCache(ApiConfig config, Map<String, Object> sqlParam, Object res) {
        //设置缓存
        if (config!=null&& StringUtils.isNotBlank(config.getCachePlugin())) {
            CachePlugin cachePlugin = PluginManager.getCachePlugin(config.getCachePlugin());
            cachePlugin.set(config, sqlParam,res);
        }
    }


    public List<Object> executeSql(Connection connection, List<ApiSql> sqlList, Map<String, Object> sqlParam, boolean flag) {

        List<Object> dataList = new ArrayList<>();
        try {
            if (flag)
                connection.setAutoCommit(false);
            else
                connection.setAutoCommit(true);
            for (ApiSql apiSql : sqlList) {
                String sqLText = apiSql.getSqlText();
                //如果有分页
                if (hasPaging(sqlParam)) {// 拼接分页语句
                    Integer pageNum = ObjectUtils.allNull(sqlParam.get("pageNum")) == true ? 1 : Integer.parseInt(String.valueOf(sqlParam.get("pageNum")));
                    Integer pageSize = ObjectUtils.allNull(sqlParam.get("pageSize")) == true ? 10 : Integer.parseInt(String.valueOf(sqlParam.get("pageSize")));
                    //LIMIT " + pageSize+ " OFFSET " +pageSize*(pageNum-1)
                    if (sqLText.lastIndexOf(";") > 0) {
                        sqLText.replace(";", "");
                    }
                    sqLText = sqLText + " LIMIT " + pageSize + " OFFSET " + ((pageNum - 1) * pageSize);
                }
                SqlMeta sqlMeta = SqlEngineUtil.getEngine().parse(sqLText, sqlParam);
                Object data = JdbcUtil.executeSql(connection, sqlMeta.getSql(), sqlMeta.getJdbcParamValues());
                dataList.add(data);
            }
            if (flag)
                connection.commit();
            return dataList;
        } catch (Exception e) {
            try {
                if (flag)
                    connection.rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
            throw new RuntimeException(e.getMessage());
        } finally {
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //是否有分页
    private Boolean hasPaging(Map<String, Object> sqlParam) {
        return (sqlParam != null && sqlParam.containsKey("pageNum") && sqlParam.containsKey("pageSize"));
    }

    public int executeCountSql(Connection connection, Map<String, Object> sqlParam, String sql) {

        String countSql = "SELECT COUNT(0) FROM (" + sql + ") as total";
        SqlMeta sqlMeta = SqlEngineUtil.getEngine().parse(countSql, sqlParam);
        PreparedStatement statement = null;
        ResultSet rs = null;
        List<Object> jdbcParamValues = sqlMeta.getJdbcParamValues();
        try {
            statement = connection.prepareStatement(sqlMeta.getSql());
            //参数注入
            for (int i = 1; i <= jdbcParamValues.size(); i++) {
                statement.setObject(i, jdbcParamValues.get(i - 1));
            }
            rs = statement.executeQuery();
            if (rs.next()) {
                return rs.getInt(1);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != statement) {
                    statement.close();
                }
                if (null != rs) {
                    rs.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return 0;
    }

    public int getTotalCount(ApiConfig config, Map<String, Object> sqlParam, ApiDatasource datasource) {
        List<ApiSql> sqlList = config.getSqlList();
        DruidPooledConnection connection = null;
        try {
            connection = PoolManager.getPooledConnection(datasource);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        String sqlText = "";
        if (sqlList != null && !sqlList.isEmpty()) {
            sqlText = sqlList.get(0).getSqlText();
        }

        int total = executeCountSql(connection, sqlParam, sqlText);
        return total;
    }

    private Map<String, Object> getParams(HttpServletRequest request, ApiConfig apiConfig) {
        String contentType = request.getContentType();
        //如果是浏览器get请求过来，取出来的contentType是null
        if (contentType == null) {
            contentType = MediaType.APPLICATION_FORM_URLENCODED_VALUE;

        }
        if (MediaType.APPLICATION_JSON_VALUE.equalsIgnoreCase(apiConfig.getContentType())) {
            contentType = MediaType.APPLICATION_JSON_VALUE;
        }
        Map<String, Object> params = new HashMap<>();
        //如果是application/json请求，不管接口规定的content-type是什么，接口都可以访问，且请求参数都以json body 为准
        if (contentType.equalsIgnoreCase(MediaType.APPLICATION_JSON_VALUE) || contentType.contains(MediaType.APPLICATION_JSON_VALUE)) {
            JSONObject jo = getHttpJsonBody(request);
            if (jo != null && !jo.isEmpty()) {
                params = JSONObject.parseObject(jo.toJSONString(), new TypeReference<Map<String, Object>>() {
                });
            }
        }
        //如果是application/x-www-form-urlencoded请求，先判断接口规定的content-type是不是确实是application/x-www-form-urlencoded
        else if (contentType.equalsIgnoreCase(MediaType.APPLICATION_FORM_URLENCODED_VALUE) || contentType.contains(MediaType.APPLICATION_FORM_URLENCODED_VALUE)) {
            if (MediaType.APPLICATION_FORM_URLENCODED_VALUE.equalsIgnoreCase(apiConfig.getContentType())) {
                params = apiService.getSqlParam(request, apiConfig);
            } else {
                throw new RuntimeException("this API only support content-type: " + apiConfig.getContentType() + ", but you use: " + contentType);
            }
        } else {
            throw new RuntimeException("content-type not supported: " + contentType);
        }

        return params;
    }

    private JSONObject getHttpJsonBody(HttpServletRequest request) {
        try {
            InputStreamReader in = new InputStreamReader(request.getInputStream(), "utf-8");
            BufferedReader br = new BufferedReader(in);
            StringBuilder sb = new StringBuilder();
            String line = null;
            while ((line = br.readLine()) != null) {
                sb.append(line);
            }
            br.close();
            JSONObject jsonObject = new JSONObject();
            if (com.coalmine.common.utils.StringUtils.isJson(String.valueOf(sb))) {
                jsonObject = JSON.parseObject(sb.toString());
            } else {
                JSONArray jsonArray = JSON.parseArray(sb.toString());
                jsonObject.put("list", jsonArray);
            }
            return jsonObject;
        } catch (Exception e) {
            log.error(e.getMessage() + e);
        } finally {

        }
        return null;
    }


    private boolean checkApiApproved(ApiConfig config, String token) {
        if (EApiPermission.PRIVATE.getCode() == config.getPrevilege()) {
            List<ApiApprove> apiApproveList = approveMapper.listByApiUser(config.getId(), token);
            ApiApprove unApproved = apiApproveList.stream()
                    .filter(s -> s.getApproveType() == EApproveStatus.PENDING.getCode()
                            || s.getApproveType() == EApproveStatus.REJECTED.getCode())
                    .findAny()
                    .orElse(null);
            if (unApproved != null) {
                return false;
            }
        }
        return true;
    }

    /**
     * 按天统计用户调用API次数
     *
     * @param log
     */
    private void apiStatByDay(ApiLog log) {
        String dateStr = DateUtils.parseDateToStr(DateUtils.YYYYMMDD, log.getCreateTime());
        final String cacheKey;
        if (log.getTokenId() != null) {
            cacheKey = CacheConstants.DATE_API_USER_STAT + ":" + dateStr + ":" + log.getApiId() + ":" + log.getTokenId();
        } else {
            // 开放的API
            cacheKey = CacheConstants.DATE_API_USER_STAT + ":" + dateStr + ":" + log.getApiId();
        }
        ApiStat apiStat = redisCache.getCacheObject(cacheKey);
        if (apiStat == null) {
            apiStat = new ApiStat();
            apiStat.setDay(dateStr);
            apiStat.setToken(log.getTokenId());
            apiStat.setApiId(log.getApiId());
            apiStat.setSuccessTimes(log.getTag() ? 1 : 0);
            apiStat.setFailureTimes(log.getTag() ? 0 : 1);
        } else {
            apiStat.setDay(apiStat.getDay());
            apiStat.setToken(apiStat.getToken());
            apiStat.setApiId(apiStat.getApiId());
            apiStat.setSuccessTimes(log.getTag() ? apiStat.getSuccessTimes() + 1 : apiStat.getSuccessTimes());
            apiStat.setFailureTimes(log.getTag() ? apiStat.getFailureTimes() : apiStat.getFailureTimes() + 1);
        }
        // 保留7天，结合定时任务，存储历史统计数据到数据库，实时数据取自redis
        redisCache.setCacheObject(cacheKey, apiStat, CacheTimeConstants.TIME_7D, TimeUnit.SECONDS);
    }


}
